package com.ktgj.customer.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ktgj.common.core.constant.Constants;
import com.ktgj.common.core.constant.CustomerConstants;
import com.ktgj.common.core.domain.R;
import com.ktgj.common.core.exception.LoginException;
import com.ktgj.common.core.utils.DateUtils;
import com.ktgj.common.core.utils.IdUtils;
import com.ktgj.common.core.utils.xml.XElement;
import com.ktgj.common.core.web.domain.AjaxResult;
import com.ktgj.common.redis.service.RedisService;
import com.ktgj.common.sms.service.CHttpPost;
import com.ktgj.common.core.utils.HttpClientUtil;
import com.ktgj.customer.domain.*;
import com.ktgj.customer.domain.dto.PositionDetailsDTO;
import com.ktgj.customer.mapper.*;
import com.ktgj.customer.service.*;
import com.ktgj.pay.api.domain.WXConfigApi;
import com.ktgj.sms.api.RemoteSmsService;
import com.ktgj.sms.api.RemoteSmsTemplateService;
import com.ktgj.sms.api.domain.Sms;
import com.ktgj.sms.api.domain.SmsTemplate;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.security.spec.AlgorithmParameterSpec;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Transactional
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private CustomerCardMapper customerCardMapper;

    @Autowired
    private RedisService redisService;
    @Autowired
    private ICardService cardService;
    @Autowired
    private ICustomerRightdetailService customerRightdetailService;
    @Autowired
    private IPointRecordService pointRecordService;
    @Autowired
    private ICardPurchaseService cardPurchaseService;

    @Autowired
    private RemoteSmsTemplateService remoteSmsTemplateService;

    @Autowired
    private RemoteSmsService remoteSmsService;

    @Autowired
    private CommonAddressesMapper commonAddressesMapper;

    @Autowired
    private EmployeePositionMapper employeePositionMapper;

    @Autowired
    private PositionMenuMapper positionMenuMapper;

    @Autowired
    private PositionMapper positionMapper;

    /**
     * redis存储周期
     */
//    private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;
    private final static long EXPIRE_TIME = 30;

    private final static long EMPLOYEE_EXPIRE_TIME = 24;

    /**
     * 员工端登录token前缀
     */
    public final static String EMPLOYEE_LOGIN_TOKEN_KEY = "employee_login_tokens:";

    /**
     * 员工登录接口
     *
     * @param loginId
     * @param loginPwd
     * @return
     */
    @Override
    public Employee employeeLogin(String loginId, String loginPwd) throws Exception {
        Employee employee = employeeMapper.employeeLogin(loginId, DigestUtils.md5Hex(loginPwd));
        if (employee != null) {//判断账号密码是否有误
            if (employee.getEmployeeStatus() == null || !employee.getEmployeeStatus().equals("quit")){
                Collection<String> keys = redisService.keys(EMPLOYEE_LOGIN_TOKEN_KEY + "*");//获取redis中，全部已登录用户的key
                for (String key : keys) {
                    String employeeJson = redisService.getCacheObject(key);
                    Employee employeeLogin = JSONObject.parseObject(employeeJson, Employee.class);//根据key逐个取出
                    if (employee.getEmployeeLoginId().equals(employeeLogin.getEmployeeLoginId())) {//判断当前登录用户是否已经登录过
                        redisService.deleteObject(key);//删除旧token
                    }
                }
                fillEmployeeRole(employee);
                String token = IdUtils.fastUUID();
                redisService.setCacheObject(EMPLOYEE_LOGIN_TOKEN_KEY + token, JSON.toJSONString(employee), EMPLOYEE_EXPIRE_TIME, TimeUnit.HOURS);//存入新的token
                employee.setToken(token);
                return employee;
            }
            else {
                throw new Exception("该员工已离职");
            }
        } else {
            return null;//账号密码错误
        }
    }

    //填充用户所有的菜单和权限信息
    public void fillEmployeeRole(Employee employee)
    {
        //处理员工多个角色和多个菜单的关联关系
        EmployeePosition employeePosition = new EmployeePosition();
        List<EmployeePosition> lstEmployeePostion = employeePositionMapper.selectEmployeePositionList(employeePosition).stream().filter(item -> item.getEmployeeId().equals(employee.getEmployeeId())).collect(Collectors.toList());
        for (EmployeePosition position :lstEmployeePostion)
        {
            EmployeePosition tmpEmployeePosition = new EmployeePosition();
            tmpEmployeePosition.setEmployeeId(employee.getEmployeeId());
            tmpEmployeePosition.setPositionId(position.getPositionId());
            PositionDetailsDTO positionDetailsDTO = positionMapper.queryPositionById(position.getPositionId());
            tmpEmployeePosition.setPositionName(positionDetailsDTO.getPositionName());
            tmpEmployeePosition.setPermission(positionDetailsDTO.getPermission());
            tmpEmployeePosition.setLstMenu(new ArrayList<>());
            tmpEmployeePosition.getLstMenu().addAll(positionDetailsDTO.getSysMenuLoginDTOS());
            employee.getEmployeePositions().add(tmpEmployeePosition);
        }
    }

    /**
     * 员工端 根据token查询用户信息
     *
     * @param request
     * @return
     */
    @Override
    public Employee getLoginEmployee(HttpServletRequest request) {
        String token = request.getHeader("token");
        String employeeJson = redisService.getCacheObject(EMPLOYEE_LOGIN_TOKEN_KEY + token);
        return JSONObject.parseObject(employeeJson, Employee.class);
    }

    /**
     * 用户登录接口
     *
     * @param loginId
     * @param loginPwd
     * @return
     */
    @Override
    public Customer customerLogin(String loginId, String loginPwd) throws Exception {
        Customer customer = customerMapper.customerLogin(loginId, DigestUtils.md5Hex(loginPwd));
        if (customer != null) {//判断账号密码是否有误
            customer.setToken(getCustomerToken(customer));

            CommonAddresses commonAddresses;
            try {
                commonAddresses = commonAddressesMapper.selectCommonAddresses(customer.getCustomerId());
            } catch (Exception e){
                throw new Exception("数据出现异常");
            }

            if (commonAddresses != null){
                customer.setPermanentResidence(commonAddresses.getDetailedAddress());
            }

            return customer;
        } else {
            return null;//账号密码有误
        }
    }

    /**
     * 用户端 根据token查询用户信息
     *
     * @param request
     * @return
     */
    @Override
    public Customer getLoginCustomer(HttpServletRequest request) throws LoginException {
        String token = request.getHeader("token");
        if (org.apache.commons.lang.StringUtils.isBlank(token)) {
            throw new LoginException("没有获取到token");
        }
        Collection<String> keys = redisService.keys("app" + "*" + token);//根据唯一的token查询出唯一key
        String customerJson = "";
        for (String key : keys) {
            customerJson = redisService.getCacheObject(key);
        }
        if (org.apache.commons.lang.StringUtils.isBlank(customerJson)) {
            throw new LoginException("登录已过期，请重新登录");
        }
        Customer customer = JSONObject.parseObject(customerJson, Customer.class);
        if (customer == null) {
            throw new LoginException("登录已过期，请重新登录");
        }
        return customer;
    }

    /**
     * 用户端 修改登录密码
     *
     * @param pwd
     * @return
     */
    @Override
    public int updatePwd(Long customerId, String pwd) {
        return customerMapper.updatePwd(customerId, DigestUtils.md5Hex(pwd));
    }

    @Override
    public int updatetrainPassword(Long customerId, String trainPassword) {
        return customerMapper.updatetrainPassword(customerId, trainPassword);
    }

    /**
     * 用户端 手机号登录
     *
     * @param phone
     * @return
     */
    @Override
    public Customer customerPhoneLogin(String phone, String code) {
        String redisCode = redisService.getCacheObject(phone + "Code");
        if (redisCode == null) {
            return null;
        }
        if (!redisCode.equals(code)) {
            return null;
        }
        Customer customer = customerMapper.customerPhoneLogin(phone);
        if (customer != null) {//查询是否存在该用户
            customer.setToken(getCustomerToken(customer));
            return customer;
        } else {
            customer = new Customer();
            customer.setContractPhoneOne(phone);
            return register(customer, "program");
        }
    }

    /**
     * 小程序登录
     * @param profileAvatar
     * @param nickname
     * @param phone
     * @param openId
     * @return
     */
    @Override
    public Customer appletLogin(String profileAvatar, String nickname, String phone, String openId) {
        Customer customer = customerMapper.selectCustomerByOpenId(openId);
        if (customer != null) {
            customer.setToken(getCustomerToken(customer));
            return customer;
        } else {
            customer = customerMapper.customerPhoneLogin(phone);
            if (customer != null) {
                customer.setOpenId(openId);
                customerMapper.updateCustomer(customer);
                customer.setToken(getCustomerToken(customer));
                return customer;
            } else {
                customer = new Customer();
                customer.setContractPhoneOne(phone);
                customer.setAvatar(profileAvatar);
                customer.setNickName(nickname);
                customer.setOpenId(openId);
                return register(customer, "program");
            }
        }
    }

    /**
     * 发送验证码
     * */
    @Override
    public String smsLoginCode(String phone) {
        //获取当前手机号发送过多少条验证码
        Integer codeLimit = redisService.getCacheObject(phone + "CodeLimit");
        if (codeLimit == null)//没有发送过
            redisService.setCacheObject(phone + "CodeLimit", 1, 6l, TimeUnit.HOURS);
        else if (codeLimit > 20)//超出限制
            return "limit";
        else//发送过但在正常范围内
            redisService.setCacheObject(phone + "CodeLimit", codeLimit + 1, 6l, TimeUnit.HOURS);

        //生成验证码 todo 会员短信息验证码因测试写死（李博洋）
        //String code = this.getRandomNumber(4);
        String code = "1111";
        //获取到短信模板
        String smsTemplate = remoteSmsTemplateService.getTemplate(1l).getData().getTemplateContent();
        //编辑模板生成待发送短信内容
        smsTemplate = smsTemplate.replaceFirst("#\\{Code}", code);

        if (!remoteSmsService.sendSmsMessage(new Sms(phone, smsTemplate)))
            return "error";
        redisService.setCacheObject(phone + "Code", code, 3l, TimeUnit.MINUTES);
        //在redis中保存验证码，等待判断用户输入的验证码,有效期3分钟
        return "success";
    }

    @Override
    public AjaxResult appletAuth(String code, HttpServletRequest request) {
        String appId = WXConfigApi.PROGRAM_APPID;
        String secret = WXConfigApi.PROGRAM_appsecret;
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code";
        String s = HttpClientUtil.doGet(url);
        System.out.println("小程序code取openId,请求返回值:" + s);
        Map data = JSON.parseObject(s, Map.class);

        Object retCode = data.get("errcode");
        if (retCode != null) {
            String errcode = retCode.toString();
            if ("-1".equals(errcode)) {
                return AjaxResult.error("00003", "微信系统繁忙");
            }
            if ("40029".equals(errcode)) {
                return AjaxResult.error("00003", "code无效");
            }
            if ("45011".equals(errcode)) {
                return AjaxResult.error("00003", "请求频率超过限制");
            }
            if ("40163".equals(errcode)) {
                return AjaxResult.error("00003", "该code已被使用");
            }
            return AjaxResult.error("00003", "请求失败,错误信息:" + s);
        }

        //Customer customer = iCustomerService.selectCustomerByOpenId(data.get("openid").toString());
        return AjaxResult.success(data);
    }

    @Override
    public AjaxResult appletDecodePhone(Map<String, String> map) {
        String code = map.get("code");
        String appId = WXConfigApi.PROGRAM_APPID;
        String secret = WXConfigApi.PROGRAM_appsecret;
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + secret;
        String s = HttpClientUtil.doGet(url);
        System.out.println("小程序getAccessToken,请求返回值:" + s);
        Map<String,String> data = JSON.parseObject(s, Map.class);
        String accessToken = data.get("access_token");

        String url2 = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
        String result = HttpClientUtil.doPostJson(url2, "{\"code\":\"" + code + "\"}");
        Map<String,String> resultData = JSON.parseObject(result, Map.class);
        return AjaxResult.success(resultData);
    }

    @Override
    public Customer getLoginCustomerByToken(String token) {
        if (org.apache.commons.lang.StringUtils.isBlank(token)) {
            throw new LoginException("没有获取到token");
        }
        Collection<String> keys = redisService.keys("app" + "*" + token);//根据唯一的token查询出唯一key
        String customerJson = "";
        for (String key : keys) {
            customerJson = redisService.getCacheObject(key);
        }
        if (org.apache.commons.lang.StringUtils.isBlank(customerJson)) {
            throw new LoginException("登录已过期，请重新登录");
        }
        Customer customer = JSONObject.parseObject(customerJson, Customer.class);
        if (customer == null) {
            throw new LoginException("登录已过期，请重新登录");
        }
        return customer;
    }

    @Override
    public Employee getLoginEmployeeByToken(String token) {
        String employeeJson = redisService.getCacheObject(EMPLOYEE_LOGIN_TOKEN_KEY + token);
        System.out.println(employeeJson);
        return JSONObject.parseObject(employeeJson, Employee.class);
    }

    /**
     * 短信修改密码
     * */
    @Override
    public int updatePwd(String newPwdOne, String newPwdTwo, Customer customer) throws Exception {
        newPwdOne = DigestUtils.md5Hex(newPwdOne);
        newPwdTwo = DigestUtils.md5Hex(newPwdTwo);
        if (newPwdOne.equals(newPwdTwo)){
            int i = customerMapper.updatePwd(customer.getCustomerId(), newPwdTwo);
            if (i > 0){
                return i;
            }
            else {
                return 0;
            }
        }
        else {
            throw new Exception("两次密码不一致，请重新输入");
        }
    }

    /**
     * 员工修改密码
     * */
    @Override
    public int updatePassword(HttpServletRequest request, String oldPwd, String newPwdOne, String newPwdTwo) throws Exception {
        Employee employee = getLoginEmployee(request);
        if (employee != null){
            oldPwd = DigestUtils.md5Hex(oldPwd);
            if (employee.getEmployeeLoginPwd().equals(oldPwd)){
                if (newPwdOne.equals(newPwdTwo)){
                    newPwdOne = DigestUtils.md5Hex(newPwdOne);
                    Employee tmpEmployee = new Employee();
                    tmpEmployee.setEmployeeId(employee.getEmployeeId());
                    tmpEmployee.setEmployeeLoginPwd(newPwdOne);
                    int i = employeeMapper.updateEmployee(tmpEmployee);
                    return i;
                }
                else {
                    throw new Exception("两次密码不一致");
                }
            }
            else {
                throw new Exception("原密码错误");
            }
        }
        else {
            throw new Exception("登录已过期");
        }
    }

    //员工退出登录
    @Override
    public boolean logOut(String token) {
        //先判断缓存中是否存在此次查询的结果
        Object redisData = redisService.getCacheObject(EMPLOYEE_LOGIN_TOKEN_KEY + token);
        if (redisData!=null) {
            redisService.deleteObject(EMPLOYEE_LOGIN_TOKEN_KEY + token);
            return true;
        }
        return false;
    }

    /**
     * 用户端 登录 redis删除旧token，存入新token并返回
     *
     * @param customer
     * @return
     */
    public String getCustomerToken(Customer customer) {
        Collection<String> oneKeys = redisService.keys("app" + customer.getContractPhoneOne() + "*");
        Collection<String> twoKeys = redisService.keys("app" + customer.getContractPhoneTow() + "*");
        Collection<String> keys = CollectionUtils.union(oneKeys, twoKeys);//获取redis中，当前账号登录后记录的key
        for (String key : keys) {
            redisService.deleteObject(key);//删除旧token
        }
        String token = IdUtils.fastUUID();
        redisService.setCacheObject("app" + customer.getContractPhoneOne() + token, JSON.toJSONString(customer), EXPIRE_TIME, TimeUnit.DAYS);//存入新的token
        return token;
    }

    /**
     * 验证码生成方法
     */
    public String getRandomNumber(int digital) {
        int[] array = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        Random rand = new Random();

        int result;
        int i;
        for (result = 10; result > 1; --result) {
            i = rand.nextInt(result);
            int tmp = array[i];
            array[i] = array[result - 1];
            array[result - 1] = tmp;
        }

        result = 0;

        for (i = 0; i < digital; ++i) {
            result = result * 10 + array[i];
        }

        return getSuffixCode(digital, result);
    }

    public static String getSuffixCode(int length, int initNum) {
        String code = "";

        for (int i = length; i > (initNum + "").length(); --i) {
            code = code + "0";
        }

        return code + initNum;
    }

    @Override
    public Customer register(Customer customer, String type) {

        Date date = DateUtils.getNowDate();

        /**
         * 注册用户
         */
        customer.setContractPhoneOne(customer.getContractPhoneOne());
        customer.setCreateTime(date);
        customer.setEmployeeIdInput(1L);
        if (type != null && !type.equals("") && type.equals("program")){
            customer.setNickName(customer.getContractPhoneOne());
            customer.setCustomerName(customer.getContractPhoneOne());
            customer.setBirthday(customer.getBirthday());
        }
        customerMapper.insertCustomer(customer);//注册用户

        pointRecordService.addPointRecord(new BigDecimal(100), "注册赠送积分", 1L, customer.getCustomerId(), CustomerConstants.register.getValue(), null, null);//(消费金额，订单描述，记录编号，员工信息ID，客户基本信息ID，对应订单类型，对应订单号，原因详细说明)

        /**
         * 默认开卡流程
         */
        Card card = cardService.selectCardAndHaveRightdetailsById(1L);

        //创建新的会员卡
        CustomerCard customerCard = new CustomerCard();
        customerCard.setCardId(card.getCardId());//卡id
        customerCard.setCardCode(cardPurchaseService.getCardCode(card));//卡号
        customerCard.setCustomerId(customer.getCustomerId());//会员id
        customerCard.setCreateEmployeeId(1L);//创建人
        customerCard.setCreateTime(date);//创建时间
        customerCard.setValidDate(date);//生效日期
        customerCard.setInvalidDate(DateUtils.addYears(date, 99));//失效日期
        customerCard.setCardStatus(CustomerConstants.valid.getValue());//卡状态
        customerCard.setCarddmansId(1L);//售卡人
        customerCard.setSalePrice(new BigDecimal(0));//售卡价格
        customerCardMapper.insertCustomerCard(customerCard);

        if (card.getLstRightDetail() != null && card.getLstRightDetail().size() != 0) {
            //新增会员权益
            //(权益集合， 会员id， 生效时间， 到期时间， 员工id， 权益来源（卡、权益包）， 会员卡id， 子订单id)
            customerRightdetailService.addCustomerRightdetail(card.getLstRightDetail(), customerCard.getCustomerId(), customerCard.getValidDate(), customerCard.getInvalidDate(),
                    1L, CustomerConstants.rightSourceCard.getValue(), customerCard.getCustomerCardId(), null, customerCard.getCardCode(), null,"");
        }

        customer.setToken(getCustomerToken(customer));//存入token
        return customer;//返回新注册用户

    }
}
